package com.haidechizi.i18n.common.support;

import com.haidechizi.i18n.common.annotation.I18nField;
import com.haidechizi.i18n.common.annotation.I18nIgnore;
import com.haidechizi.i18n.common.annotation.I18nModel;
import com.haidechizi.i18n.common.enums.annotation.I18nEnumField;
import com.haidechizi.i18n.common.utils.I18nFieldUtils;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.reflect.FieldUtils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 反射缓存
 * 缓存一部分反射属性，加快国际化速度
 */
public class I18nReflectUtils {

    private static final Map<Class<?>, List<Field>> simpleFieldsMap = new ConcurrentHashMap<>(64);

    private static final Map<Class<?>, List<Field>> combinedFieldsMap = new ConcurrentHashMap<>(32);

    private static final Map<Class<?>, List<Field>> customFieldsMap = new ConcurrentHashMap<>();

    private static final Map<Class<?>, List<Field>> otherFieldsMap = new ConcurrentHashMap<>(64);

    private static final Map<Class<?>, I18nModel> i18nModelAnnotationMap = new ConcurrentHashMap<>(64);

    private static final Map<Field, I18nField> fieldAnnotationMap = new ConcurrentHashMap<>(256);

    private static final Map<Class<?>, Boolean> classHasInitMap = new ConcurrentHashMap<>(64);

    private static final Map<Class<?>, Boolean> classFieldHasInitMap = new ConcurrentHashMap<>(32);

    // 增加枚举类的国际化
    private static final Map<Class<?>, List<Field>> enumFieldsMap = new ConcurrentHashMap<>();

    private static final Map<Field, I18nEnumField> enumFieldAnnotationMap = new ConcurrentHashMap<>(256);

    /**
     * 获取类上的I18nModel注解，带缓存
     *
     * @param clazz
     * @return
     */
    public static I18nModel getI18nModelAnnotation(Class<?> clazz) {
        I18nModel i18nModel = i18nModelAnnotationMap.get(clazz);
        if (i18nModel == null) {
            Boolean init = classHasInitMap.getOrDefault(clazz, false);
            if (init) {
                return null;
            }
            synchronized (clazz) {
                if (init) {
                    return i18nModelAnnotationMap.get(clazz);
                }
                if (clazz.isAnnotationPresent(I18nModel.class)) {
                    i18nModel = clazz.getAnnotation(I18nModel.class);
                    i18nModelAnnotationMap.put(clazz, i18nModel);
                }
                classHasInitMap.put(clazz, true);
            }

        }
        return i18nModel;
    }

    /**
     * 获取field上的I18nField注解
     * <p>
     * 获取属性之前，肯定已经先进行了初始化
     *
     * @param field
     * @return
     * @see I18nReflectUtils#init(Class)
     */
    public static I18nField getI18nFieldAnnotation(Field field) {
        return fieldAnnotationMap.get(field);
    }

    /**
     * 获取field上带I18nField注解的field，带缓存
     *
     * @param clazz
     * @return
     */
    public static List<Field> getSimpleFieldsListWithAnnotation(Class<?> clazz) {
        List<Field> fields = simpleFieldsMap.get(clazz);
        if (classFieldHasInitMap.get(clazz)) {
            return fields;
        }
        if (fields == null) {
            init(clazz);
            fields = simpleFieldsMap.get(clazz);
        }
        return fields;
    }

    /**
     * 获取没有标注注解，但是field的type上标注了注解的field
     * 带缓存
     *
     * @param clazz
     * @return
     */
    public static List<Field> getOtherFieldsListWithAnnotation(Class<?> clazz) {
        List<Field> fields = otherFieldsMap.get(clazz);
        if (classFieldHasInitMap.get(clazz)) {
            return fields;
        }
        if (fields == null) {
            init(clazz);
            fields = otherFieldsMap.get(clazz);
        }
        return fields;
    }

    /**
     * 获取自定义国际化的field
     *
     * @param clazz
     * @return
     */
    public static List<Field> getCustomFieldsListWithAnnotation(Class<?> clazz) {
        List<Field> fields = customFieldsMap.get(clazz);
        if (classFieldHasInitMap.get(clazz)) {
            return fields;
        }
        if (fields == null) {
            init(clazz);
            fields = customFieldsMap.get(clazz);
        }
        return fields;
    }

    /**
     * 获取组合的field
     *
     * @param clazz
     * @return
     */
    public static List<Field> getCombinedFieldsListWithAnnotation(Class<?> clazz) {
        List<Field> fields = combinedFieldsMap.get(clazz);
        if (classFieldHasInitMap.get(clazz)) {
            return fields;
        }
        if (fields == null) {
            init(clazz);
            fields = combinedFieldsMap.get(clazz);
        }
        return fields;
    }

    /**
     * 初始化缓存
     *
     * @param clazz
     */
    private static void init(Class<?> clazz) {
        Boolean init = classFieldHasInitMap.getOrDefault(clazz, false);
        if (init) {
            return;
        }
        synchronized (clazz) {
            if (init) {
                return;
            }
            List<Field> fieldsListWithAnnotation = FieldUtils.getFieldsListWithAnnotation(clazz, I18nField.class);
            List<Field> simpleFields =
                    fieldsListWithAnnotation.stream().filter(I18nFieldUtils::isSimpleField).collect(Collectors.toList());
            if (!fieldsListWithAnnotation.isEmpty()) {
                simpleFieldsMap.put(clazz, simpleFields);
                simpleFields.forEach(field -> {
                    I18nField i18nField = field.getAnnotation(I18nField.class);
                    fieldAnnotationMap.put(field, i18nField);
                });

                List<Field> customFields =
                        fieldsListWithAnnotation.stream().filter(I18nFieldUtils::isCustomField).collect(Collectors.toList());

                customFieldsMap.put(clazz, customFields);

                customFields.forEach(field -> {
                    I18nField i18nField = field.getAnnotation(I18nField.class);
                    fieldAnnotationMap.put(field, i18nField);
                });

                List<Field> combinedFields =
                        fieldsListWithAnnotation.stream().filter(I18nFieldUtils::isCombinedField).collect(Collectors.toList());
                combinedFieldsMap.put(clazz, combinedFields);

                combinedFields.forEach(field -> {
                    I18nField i18nField = field.getAnnotation(I18nField.class);
                    fieldAnnotationMap.put(field, i18nField);
                });
            } else {
                simpleFieldsMap.put(clazz, new ArrayList<>());
                customFieldsMap.put(clazz, new ArrayList<>());
                combinedFieldsMap.put(clazz, new ArrayList<>());
            }

            List<Field> i18nEnumFields = FieldUtils.getFieldsListWithAnnotation(clazz, I18nEnumField.class);
            if (CollectionUtils.isNotEmpty(i18nEnumFields)) {
                enumFieldsMap.put(clazz, i18nEnumFields);
                i18nEnumFields.forEach(field -> {
                    I18nEnumField annotation = field.getAnnotation(I18nEnumField.class);
                    enumFieldAnnotationMap.put(field, annotation);
                });
            } else {
                enumFieldsMap.put(clazz, new ArrayList<>());
            }

            List<Field> ignoreField = FieldUtils.getFieldsListWithAnnotation(clazz, I18nIgnore.class);

            List<Field> allFieldsList = FieldUtils.getAllFieldsList(clazz);
            List<Field> fieldList = allFieldsList
                    .stream()
                    .filter(el -> !simpleFields.contains(el))
                    .filter(el -> !ignoreField.contains(el))
                    .filter(el -> !el.getType().getName().contains("Long"))
                    .filter(el -> !el.getType().getName().contains("Boolean"))
                    .filter(el -> !el.getType().getName().contains("Float"))
                    .filter(el -> !el.getType().getName().contains("Date"))
                    .filter(el -> !el.getType().getName().contains("BigDecimal"))
                    .filter(el -> !el.getType().getName().contains("Integer"))
                    .filter(el -> !el.getType().getName().contains("int"))
                    .filter(el -> !el.getType().getName().contains("float"))
                    .filter(el -> !el.getType().getName().contains("Double"))
                    .filter(el -> !el.getType().getName().contains("double"))
                    .filter(el -> !el.getType().getName().contains("boolean"))
                    .filter(el -> !el.getType().getName().contains("String"))
                    .filter(el -> !el.getType().getName().contains("long"))
                    .filter(el -> !el.getType().getName().contains("Byte"))
                    .filter(el -> !el.getType().getName().contains("byte"))
                    .collect(Collectors.toList());
            otherFieldsMap.put(clazz, fieldList);
            setClassFieldHasInitMap(clazz, true);
        }
    }

    /**
     * 添加类上的注解
     *
     * @param clazz
     * @param i18nModel
     * @return
     */
    static void setI18nModelAnnotation(Class<?> clazz, I18nModel i18nModel) {
        i18nModelAnnotationMap.put(clazz, i18nModel);
    }

    /**
     * 添加field上的注解
     *
     * @param field
     * @return
     */
    static void setI18nFieldAnnotation(Field field, I18nField i18nField) {
        fieldAnnotationMap.put(field, i18nField);
    }

    /**
     * 获取field上带I18nField注解的field，带缓存
     *
     * @param clazz
     * @return
     */
    static synchronized void setSimpleFieldsListWithAnnotation(Class<?> clazz, Field field) {
        List<Field> fields = simpleFieldsMap.get(clazz);
        if (fields == null) {
            fields = new ArrayList<>();
            simpleFieldsMap.put(clazz, fields);
        }
        fields.add(field);
    }

    /**
     * 获取没有标注注解，但是field的type上标注了注解的field
     * 带缓存
     *
     * @param clazz
     * @return
     */
    static synchronized void setOtherFieldsListWithAnnotation(Class<?> clazz, Field field) {
        List<Field> fields = otherFieldsMap.get(clazz);
        if (fields == null) {
            fields = new ArrayList<>();
            otherFieldsMap.put(clazz, fields);
        }
        fields.add(field);

    }

    /**
     * 获取自定义国际化的field
     *
     * @param clazz
     * @return
     */
    static synchronized void setCustomFieldsListWithAnnotation(Class<?> clazz, Field field) {
        List<Field> fields = customFieldsMap.get(clazz);
        if (fields == null) {
            fields = new ArrayList<>();
            customFieldsMap.put(clazz, fields);
        }
        fields.add(field);

    }

    /**
     * 获取组合的field
     *
     * @param clazz
     * @return
     */
    static synchronized void setCombinedFieldsListWithAnnotation(Class<?> clazz, Field field) {
        List<Field> fields = combinedFieldsMap.get(clazz);
        if (fields == null) {
            fields = new ArrayList<>();
            combinedFieldsMap.put(clazz, fields);
        }
        fields.add(field);

    }

    static void setClassFieldHasInitMap(Class<?> clazz, boolean init) {
        classFieldHasInitMap.put(clazz, init);
    }

    public static final List<Field> getEnumFields(Class<?> clazz) {
        List<Field> fields = enumFieldsMap.get(clazz);
        if (classFieldHasInitMap.get(clazz)) {
            return fields;
        }
        if (fields == null) {
            init(clazz);
            fields = enumFieldsMap.get(clazz);
        }
        return fields;
    }

    public static final I18nEnumField getEnumFieldAnnotation(Field field) {
        return enumFieldAnnotationMap.get(field);
    }
}
